The possible impact of weather on crimes in Amsterdam#

Hide code cell source
import pandas as pd
from pandas import Timedelta
import numpy as np
import plotly.express as px
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from IPython.display import display, Markdown
import plotly.io as pio
pio.renderers.default = "notebook"
import plotly.graph_objects as go
df = pd.read_csv("merged_weather_misdrijven_monthly_v3.csv")

People have studied both human behavior and weather for centuries. One interesting question that brings these two topics together is whether weather conditions can influence the chances of people committing crimes.

Some researchers believe they do. Anderson (2001), for example, found that high temperatures can increase aggression. In our research, we explore whether weather factors like heat and rain are linked to changes in reported crime. We focus on Amsterdam, a densely populated city with detailed data.

We used daily weather data from 2012 to 2025 collected at Schiphol Airport (KNMI, 2025), and monthly crime reports from Statistics Netherlands (CBS, 2025). This allows us to investigate both general patterns, such as seasonal trends in serious crimes, and more specific ones like whether warmer weather relates to bike theft.

Through data visualizations, we explore how temperature may affect different crimes. These graphs form the basis for deeper analysis, helping us assess whether crime patterns are influenced by weather or largely independent of it.

Perspective 1: Pleasant weather increases the risk of certain crimes#

Pleasant weather often brings people outside, increasing public activity in parks, streets, and recreational areas. This rise in outdoor presence may also create more opportunities for crimes such as bicycle theft, pickpocketing, or alcohol-related incidents. In this section, we examine how sunshine and higher temperatures might correlate with these types of crime.

Argument 1.1 Higher temperatures lead to more bicycle theft#

In Amsterdam, cycling is the main form of transport. Better weather conditions encourage more outside activity such as events or going to the beach, which means more transport and bikes being left outside. This should be the optimal climate for bicycle theft and supports the idea that weather has an influence on crime. To look into this relationship between theft and tropical weather, we inspect temperature and the rates of bicycle theft in the graph below.

Hide code cell source
df.columns = df.columns.str.strip()
df['TX'] = df['TX'] / 10  # TX is in tienden van °C
df['TG'] = df['TG'] / 10  # TG is in tienden van °C
df['SQ'] = df['SQ'] / 10  # TG is in tienden van °C
display(Markdown("### Bicycle Theft vs. Maximum Temperature"))
display(Markdown("_Slight increase in thefts when it's warmer._"))

# 📈 Plot maken
plt.figure(figsize=(8, 5))
sns.regplot(
    x='TX',
    y='1.2.3 Diefstal van brom-, snor-, fietsen',
    data=df,
    scatter_kws={'alpha': 0.5}
)
plt.xlabel("Maximum Temperature (°C)")
plt.ylabel("Number of Bicycle Thefts")
plt.grid(True)
plt.tight_layout()
caption = (
    "Figure: Each dot represents monthly bicycle thefts and maximum temperature. "
    "The blue line shows a moderate positive trend between temperature and thefts."
)
plt.figtext(0.5, -0.1, caption, wrap=True, horizontalalignment='center', fontsize=10)

plt.show()

Bicycle Theft vs. Maximum Temperature

Slight increase in thefts when it’s warmer.

../_images/66b05c60e0dea71cc033455e1c1d3690cae39ce2551a7053a8063ffd96dd12e0.png

This scatterplot shows the relationship between monthly maximum temperature (°C) on the horizontal axis and the number of bicycle thefts reported in the Netherlands on the vertical axis. Each point represents one month. The blue regression line indicates a slight upward trend: bicycle thefts do appear to increase when temperatures are higher. Pearson’s correlation between the two is 0.502, which is considered moderate. To put it more intuitively: every single degree that a day gets warmer, an average of 13.23 more bicycles are stolen. This result supports the first argument, since warmer weather seems to mean more bike theft.

Perspective 2: Cold or bad weather influences other crimes (or none at all)#

An alternative perspective shows that, although specific categories of crime may be affected by warm weather conditions, others could be more common in colder, darker periods. There might also be crimes that do not show patterns with the weather at all. In this section, we explore how low visibility might relate to burglary, and whether certain types of crime show seasonal patterns, regardless of temperature.

Argument 2.1: Reduced visbility increases burglary risk#

It is expectable that foggy or dark days may be associated with more home invasions in a given region. The reason being that dark days mean reduced visibility, which makes it the perfect time for burglars to act. The weather may be a criminal’s biggest friend. To actually prove this we need to check the correlation between the number of home invasions and minimum visibility per month.

Hide code cell source
from IPython.display import display, Markdown
import matplotlib.pyplot as plt
import seaborn as sns

# Titel en korte take-away boven de plot
display(Markdown("### Lower Visibility vs. Home Burglaries"))
display(Markdown("_Lower visibility may be linked to increased burglary incidents._"))

plt.figure(figsize=(10,6))

sns.regplot(
    data=df,
    x='VVN',
    y='1.1.1 Diefstal/inbraak woning',
    scatter_kws={'s':50, 'alpha':0.6},
    line_kws={'color':'red'}
)


plt.xlabel('Average Monthly Minimum Visibility (VVN scale, higher = better visibility)', fontsize=14)
plt.ylabel('Total Burglary Incidents per Month', fontsize=14)
plt.grid(True)

# Caption onderaan, gecentreerd en iets kleiner
caption = (
    "Scatterplot shows monthly data points with visibility on the x-axis "
    "and burglary incidents on the y-axis. The red line represents the regression trend."
)
plt.gcf().text(0.5, -0.1, caption, ha='center', fontsize=10)

plt.tight_layout()
plt.show()

Lower Visibility vs. Home Burglaries

Lower visibility may be linked to increased burglary incidents.

../_images/aabe511e8e2f1398e4b363cc12214171fe4bec215ffc3fd3b180ab42cf4487dc.png

This scatterplot shows the relationship between average monthly visibility and reported burglary incidents. A negative trend suggests burglaries increase slightly in months with lower visibility. Although the Pearson correlation is weak (r = -0.318), the pattern aligns with the idea that poor visibility may support intrusions.

Argument 2.2: Serious crimes are not weather-dependent#

As for more serious crime, generally speaking, there is no obvious reason why it should depend on seasonal weather. Murder, abuse should not be associated with seasonal patterns. Warm days may lead to more activity and opportunity to commit these crimes, while cold days in turn may enable more of a melancholic mental state that motivates them more. Cybercrime sounds like an indoor activity, creating the expectation of more cybercrime in the winter, but no such correlation was found. This supports the claim that crime is not influenced by weather or seasonality.

Hide code cell source
# Map each month number to a season
season_map = {
    1: 'Winter', 2: 'Winter', 12: 'Winter',
    3: 'Spring', 4: 'Spring', 5: 'Spring',
    6: 'Summer', 7: 'Summer', 8: 'Summer',
    9: 'Autumn', 10: 'Autumn', 11: 'Autumn'
}

# Extract numeric month from 'year_month' and map to season
df['month'] = pd.to_datetime(df['year_month']).dt.month
df['Season'] = df['month'].map(season_map)

# Serious crime columns and their display names
display(Markdown("### Seasonal Distribution of Serious Crimes"))
display(Markdown("_Serious crimes like murder, abuse, and cybercrime show relatively steady distribution across seasons._"))

serious_crimes = {
    '1.4.2 Moord, doodslag': 'Murder',
    '1.4.5 Mishandeling': 'Abuse',
    '3.7.4 Cybercrime': 'Cybercrime'
}

fig, axes = plt.subplots(1, 3, figsize=(18, 6))

for ax, (col, label) in zip(axes, serious_crimes.items()):
    season_totals = df.groupby('Season')[col].sum().reindex(['Winter', 'Spring', 'Summer', 'Autumn'])
    ax.pie(
        season_totals,
        labels=season_totals.index,
        autopct='%1.1f%%',
        startangle=90,
        colors=sns.color_palette("pastel")[0:4]
    )
    ax.set_title(f'Seasonal Distribution of {label}', fontsize=14)


# Caption onder de figuur (iets lager dan tight_layout)
caption = (
    "Each pie chart shows the proportion of incidents per season for three serious crimes. "
    "Values are total counts summed over all years. "
    "Colors represent Winter, Spring, Summer, and Autumn respectively."
)
fig.text(0.5, -0.05, caption, ha='center', fontsize=15)

plt.tight_layout(rect=[0, 0.05, 1, 0.95])  # Ruimte onder voor caption
plt.show()

Seasonal Distribution of Serious Crimes

Serious crimes like murder, abuse, and cybercrime show relatively steady distribution across seasons.

../_images/fcfa7a8a38a44d742debd75337f92391656c08fdc4b4f16a4d0ec8150c3a60b6.png

These pie charts show the seasonal distribution of monthly incidents for murder, abuse, and cybercrime. If these crimes were season-dependent, large differences would be visible, especially between summer and winter. However, the distributions are fairly even across all seasons. Winter appears slightly lower due to February having fewer days. Cybercrime might occur a bit more in winter, but overall, no substantial seasonal pattern is visible.

Conclusion#

As shown earlier, certain crime types, like bike theft or substance-related incidents on boats, show notable correlations with weather. Warmer and brighter days can influence behavior in ways relevant to crime prediction. In contrast, serious crimes show little to no weather dependency. To highlight this, we compare how various crimes relate to monthly maximum temperature alongside ‘under influence on water’ incidents.

Hide code cell source
import plotly.graph_objects as go
from IPython.display import display, Markdown

crime_columns = {
    'Burglary (home)': '1.1.1 Diefstal/inbraak woning',
    'Traffic Accidents': '1.3.1 Ongevallen (weg)',
    'Under Influence (water)': '3.4.2 Onder invloed (water)',
    'Murder / Manslaughter': '1.4.2 Moord, doodslag',
    'Shoplifting': '2.5.2 Winkeldiefstal',
    'Pickpocketing': '1.2.4 Zakkenrollerij',
    'Bicycle Theft': '1.2.3 Diefstal van brom-, snor-, fietsen'    
}

def frange(start, stop, step):
    while start <= stop:
        yield round(start, 1)
        start += step

def create_slider_plot(df):
    temps = [round(t, 1) for t in list(frange(5.0, 26.5, 0.5))]
    fig = go.Figure()

    # Voeg alle frames toe, één per temperatuur
    frames = []
    for temp in temps:
        lower, upper = temp - 0.5, temp + 0.5
        filtered = df[(df['TX'] >= lower) & (df['TX'] < upper)]
        y = [
            (filtered[col].sum() / df[col].sum()) * 100 if df[col].sum() > 0 else 0
            for col in crime_columns.values()
        ]
        frames.append(go.Frame(
            data=[go.Bar(x=list(crime_columns.keys()), y=y)],
            name=f"{temp}"
        ))

    # Voeg eerste data toe als initiele trace
    fig.add_trace(frames[0].data[0])

    # Zet layout, slider en frames
    fig.update_layout(
        yaxis=dict(range=[0, 25], title="Pct of Total Incidents"),
        xaxis_title="Crime Type",
        width=800,
        height=600,
        margin=dict(b=130),  # Extra ruimte onderaan voor slider
        updatemenus=[dict(
            type="buttons",
            showactive=False,
            buttons=[dict(label="Play", method="animate",
                          args=[None, {
                              "frame": {"duration": 300, "redraw": True},
                              "fromcurrent": True,
                              "transition": {"duration": 0}
                          }])]
        )],
        sliders=[dict(
            active=temps.index(5.0),
            currentvalue={"prefix": "Temp: "},
            pad={"t": 80},  # Verplaatst slider omlaag
            steps=[dict(label=f"{t}°C", method="animate", args=[[str(t)], {
                "frame": {"duration": 0},
                "mode": "immediate"
            }]) for t in temps]
        )]
    )

    fig.frames = frames
    return fig

# Titel en korte conclusie boven de interactieve plot
display(Markdown("### Crime Distribution Changes with Temperature"))
display(Markdown("_Different weather only encourages very specific types of crime, most are evenly distributed._"))

# Plot tonen
fig = create_slider_plot(df)
fig.show()

Crime Distribution Changes with Temperature

Different weather only encourages very specific types of crime, most are evenly distributed.

It is clearly visible, that out of this list ‘under influence (water)’ is the only crime type that is really influenced by the temperature. All other categories fluctuate a little, but are relatively evenly distributed across all temperatures. This suggests that while certain crimes are sensitive to weather changes, most serious crimes are largely unaffected by temperature variations and other factors like visibility and rainfall.

References#

  1. Anderson, C. A. (2001). Heat and violence. Current directions in psychological science, 10 (1), 33–38 (https://doi.org/10.1111/1467-8721.00109).

  2. Centraal Bureau voor de Statistiek. (2025). Maandcijfers geregistreerde criminaliteit Amsterdam [dataset] [Accessed on June 10th 2025], (https://opendata.cbs.nl/statline/#/CBS/nl/dataset/83648NED/table?fromstatweb).

  3. Koninklijk Nederlands Meteorologisch Instituut. (2025). Daggegevens van het weer in Nederland [dataset] [Accessed on June 10th 2025], (https://www.knmi.nl/nederland-nu/klimatologie/daggegevens).